
unit Book;

interface

uses
  SysUtils, Classes;

function PolyglotKey(const APosition: string): UInt64;
function BestMove(const AKey: UInt64; const ABookName: TFileName): string; overload;
function BestMove(const APosition: string; const ABookName: TFileName): string; overload;

implementation

type
  U64 = UInt64;

const
  Random64: array[0..780] of UInt64 = (
    U64($9D39247E33776D41), U64($2AF7398005AAA5C7), U64($44DB015024623547), U64($9C15F73E62A76AE2),
    U64($75834465489C0C89), U64($3290AC3A203001BF), U64($0FBBAD1F61042279), U64($E83A908FF2FB60CA),
    U64($0D7E765D58755C10), U64($1A083822CEAFE02D), U64($9605D5F0E25EC3B0), U64($D021FF5CD13A2ED5),
    U64($40BDF15D4A672E32), U64($011355146FD56395), U64($5DB4832046F3D9E5), U64($239F8B2D7FF719CC),
    U64($05D1A1AE85B49AA1), U64($679F848F6E8FC971), U64($7449BBFF801FED0B), U64($7D11CDB1C3B7ADF0),
    U64($82C7709E781EB7CC), U64($F3218F1C9510786C), U64($331478F3AF51BBE6), U64($4BB38DE5E7219443),
    U64($AA649C6EBCFD50FC), U64($8DBD98A352AFD40B), U64($87D2074B81D79217), U64($19F3C751D3E92AE1),
    U64($B4AB30F062B19ABF), U64($7B0500AC42047AC4), U64($C9452CA81A09D85D), U64($24AA6C514DA27500),
    U64($4C9F34427501B447), U64($14A68FD73C910841), U64($A71B9B83461CBD93), U64($03488B95B0F1850F),
    U64($637B2B34FF93C040), U64($09D1BC9A3DD90A94), U64($3575668334A1DD3B), U64($735E2B97A4C45A23),
    U64($18727070F1BD400B), U64($1FCBACD259BF02E7), U64($D310A7C2CE9B6555), U64($BF983FE0FE5D8244),
    U64($9F74D14F7454A824), U64($51EBDC4AB9BA3035), U64($5C82C505DB9AB0FA), U64($FCF7FE8A3430B241),
    U64($3253A729B9BA3DDE), U64($8C74C368081B3075), U64($B9BC6C87167C33E7), U64($7EF48F2B83024E20),
    U64($11D505D4C351BD7F), U64($6568FCA92C76A243), U64($4DE0B0F40F32A7B8), U64($96D693460CC37E5D),
    U64($42E240CB63689F2F), U64($6D2BDCDAE2919661), U64($42880B0236E4D951), U64($5F0F4A5898171BB6),
    U64($39F890F579F92F88), U64($93C5B5F47356388B), U64($63DC359D8D231B78), U64($EC16CA8AEA98AD76),
    U64($5355F900C2A82DC7), U64($07FB9F855A997142), U64($5093417AA8A7ED5E), U64($7BCBC38DA25A7F3C),
    U64($19FC8A768CF4B6D4), U64($637A7780DECFC0D9), U64($8249A47AEE0E41F7), U64($79AD695501E7D1E8),
    U64($14ACBAF4777D5776), U64($F145B6BECCDEA195), U64($DABF2AC8201752FC), U64($24C3C94DF9C8D3F6),
    U64($BB6E2924F03912EA), U64($0CE26C0B95C980D9), U64($A49CD132BFBF7CC4), U64($E99D662AF4243939),
    U64($27E6AD7891165C3F), U64($8535F040B9744FF1), U64($54B3F4FA5F40D873), U64($72B12C32127FED2B),
    U64($EE954D3C7B411F47), U64($9A85AC909A24EAA1), U64($70AC4CD9F04F21F5), U64($F9B89D3E99A075C2),
    U64($87B3E2B2B5C907B1), U64($A366E5B8C54F48B8), U64($AE4A9346CC3F7CF2), U64($1920C04D47267BBD),
    U64($87BF02C6B49E2AE9), U64($092237AC237F3859), U64($FF07F64EF8ED14D0), U64($8DE8DCA9F03CC54E),
    U64($9C1633264DB49C89), U64($B3F22C3D0B0B38ED), U64($390E5FB44D01144B), U64($5BFEA5B4712768E9),
    U64($1E1032911FA78984), U64($9A74ACB964E78CB3), U64($4F80F7A035DAFB04), U64($6304D09A0B3738C4),
    U64($2171E64683023A08), U64($5B9B63EB9CEFF80C), U64($506AACF489889342), U64($1881AFC9A3A701D6),
    U64($6503080440750644), U64($DFD395339CDBF4A7), U64($EF927DBCF00C20F2), U64($7B32F7D1E03680EC),
    U64($B9FD7620E7316243), U64($05A7E8A57DB91B77), U64($B5889C6E15630A75), U64($4A750A09CE9573F7),
    U64($CF464CEC899A2F8A), U64($F538639CE705B824), U64($3C79A0FF5580EF7F), U64($EDE6C87F8477609D),
    U64($799E81F05BC93F31), U64($86536B8CF3428A8C), U64($97D7374C60087B73), U64($A246637CFF328532),
    U64($043FCAE60CC0EBA0), U64($920E449535DD359E), U64($70EB093B15B290CC), U64($73A1921916591CBD),
    U64($56436C9FE1A1AA8D), U64($EFAC4B70633B8F81), U64($BB215798D45DF7AF), U64($45F20042F24F1768),
    U64($930F80F4E8EB7462), U64($FF6712FFCFD75EA1), U64($AE623FD67468AA70), U64($DD2C5BC84BC8D8FC),
    U64($7EED120D54CF2DD9), U64($22FE545401165F1C), U64($C91800E98FB99929), U64($808BD68E6AC10365),
    U64($DEC468145B7605F6), U64($1BEDE3A3AEF53302), U64($43539603D6C55602), U64($AA969B5C691CCB7A),
    U64($A87832D392EFEE56), U64($65942C7B3C7E11AE), U64($DED2D633CAD004F6), U64($21F08570F420E565),
    U64($B415938D7DA94E3C), U64($91B859E59ECB6350), U64($10CFF333E0ED804A), U64($28AED140BE0BB7DD),
    U64($C5CC1D89724FA456), U64($5648F680F11A2741), U64($2D255069F0B7DAB3), U64($9BC5A38EF729ABD4),
    U64($EF2F054308F6A2BC), U64($AF2042F5CC5C2858), U64($480412BAB7F5BE2A), U64($AEF3AF4A563DFE43),
    U64($19AFE59AE451497F), U64($52593803DFF1E840), U64($F4F076E65F2CE6F0), U64($11379625747D5AF3),
    U64($BCE5D2248682C115), U64($9DA4243DE836994F), U64($066F70B33FE09017), U64($4DC4DE189B671A1C),
    U64($51039AB7712457C3), U64($C07A3F80C31FB4B4), U64($B46EE9C5E64A6E7C), U64($B3819A42ABE61C87),
    U64($21A007933A522A20), U64($2DF16F761598AA4F), U64($763C4A1371B368FD), U64($F793C46702E086A0),
    U64($D7288E012AEB8D31), U64($DE336A2A4BC1C44B), U64($0BF692B38D079F23), U64($2C604A7A177326B3),
    U64($4850E73E03EB6064), U64($CFC447F1E53C8E1B), U64($B05CA3F564268D99), U64($9AE182C8BC9474E8),
    U64($A4FC4BD4FC5558CA), U64($E755178D58FC4E76), U64($69B97DB1A4C03DFE), U64($F9B5B7C4ACC67C96),
    U64($FC6A82D64B8655FB), U64($9C684CB6C4D24417), U64($8EC97D2917456ED0), U64($6703DF9D2924E97E),
    U64($C547F57E42A7444E), U64($78E37644E7CAD29E), U64($FE9A44E9362F05FA), U64($08BD35CC38336615),
    U64($9315E5EB3A129ACE), U64($94061B871E04DF75), U64($DF1D9F9D784BA010), U64($3BBA57B68871B59D),
    U64($D2B7ADEEDED1F73F), U64($F7A255D83BC373F8), U64($D7F4F2448C0CEB81), U64($D95BE88CD210FFA7),
    U64($336F52F8FF4728E7), U64($A74049DAC312AC71), U64($A2F61BB6E437FDB5), U64($4F2A5CB07F6A35B3),
    U64($87D380BDA5BF7859), U64($16B9F7E06C453A21), U64($7BA2484C8A0FD54E), U64($F3A678CAD9A2E38C),
    U64($39B0BF7DDE437BA2), U64($FCAF55C1BF8A4424), U64($18FCF680573FA594), U64($4C0563B89F495AC3),
    U64($40E087931A00930D), U64($8CFFA9412EB642C1), U64($68CA39053261169F), U64($7A1EE967D27579E2),
    U64($9D1D60E5076F5B6F), U64($3810E399B6F65BA2), U64($32095B6D4AB5F9B1), U64($35CAB62109DD038A),
    U64($A90B24499FCFAFB1), U64($77A225A07CC2C6BD), U64($513E5E634C70E331), U64($4361C0CA3F692F12),
    U64($D941ACA44B20A45B), U64($528F7C8602C5807B), U64($52AB92BEB9613989), U64($9D1DFA2EFC557F73),
    U64($722FF175F572C348), U64($1D1260A51107FE97), U64($7A249A57EC0C9BA2), U64($04208FE9E8F7F2D6),
    U64($5A110C6058B920A0), U64($0CD9A497658A5698), U64($56FD23C8F9715A4C), U64($284C847B9D887AAE),
    U64($04FEABFBBDB619CB), U64($742E1E651C60BA83), U64($9A9632E65904AD3C), U64($881B82A13B51B9E2),
    U64($506E6744CD974924), U64($B0183DB56FFC6A79), U64($0ED9B915C66ED37E), U64($5E11E86D5873D484),
    U64($F678647E3519AC6E), U64($1B85D488D0F20CC5), U64($DAB9FE6525D89021), U64($0D151D86ADB73615),
    U64($A865A54EDCC0F019), U64($93C42566AEF98FFB), U64($99E7AFEABE000731), U64($48CBFF086DDF285A),
    U64($7F9B6AF1EBF78BAF), U64($58627E1A149BBA21), U64($2CD16E2ABD791E33), U64($D363EFF5F0977996),
    U64($0CE2A38C344A6EED), U64($1A804AADB9CFA741), U64($907F30421D78C5DE), U64($501F65EDB3034D07),
    U64($37624AE5A48FA6E9), U64($957BAF61700CFF4E), U64($3A6C27934E31188A), U64($D49503536ABCA345),
    U64($088E049589C432E0), U64($F943AEE7FEBF21B8), U64($6C3B8E3E336139D3), U64($364F6FFA464EE52E),
    U64($D60F6DCEDC314222), U64($56963B0DCA418FC0), U64($16F50EDF91E513AF), U64($EF1955914B609F93),
    U64($565601C0364E3228), U64($ECB53939887E8175), U64($BAC7A9A18531294B), U64($B344C470397BBA52),
    U64($65D34954DAF3CEBD), U64($B4B81B3FA97511E2), U64($B422061193D6F6A7), U64($071582401C38434D),
    U64($7A13F18BBEDC4FF5), U64($BC4097B116C524D2), U64($59B97885E2F2EA28), U64($99170A5DC3115544),
    U64($6F423357E7C6A9F9), U64($325928EE6E6F8794), U64($D0E4366228B03343), U64($565C31F7DE89EA27),
    U64($30F5611484119414), U64($D873DB391292ED4F), U64($7BD94E1D8E17DEBC), U64($C7D9F16864A76E94),
    U64($947AE053EE56E63C), U64($C8C93882F9475F5F), U64($3A9BF55BA91F81CA), U64($D9A11FBB3D9808E4),
    U64($0FD22063EDC29FCA), U64($B3F256D8ACA0B0B9), U64($B03031A8B4516E84), U64($35DD37D5871448AF),
    U64($E9F6082B05542E4E), U64($EBFAFA33D7254B59), U64($9255ABB50D532280), U64($B9AB4CE57F2D34F3),
    U64($693501D628297551), U64($C62C58F97DD949BF), U64($CD454F8F19C5126A), U64($BBE83F4ECC2BDECB),
    U64($DC842B7E2819E230), U64($BA89142E007503B8), U64($A3BC941D0A5061CB), U64($E9F6760E32CD8021),
    U64($09C7E552BC76492F), U64($852F54934DA55CC9), U64($8107FCCF064FCF56), U64($098954D51FFF6580),
    U64($23B70EDB1955C4BF), U64($C330DE426430F69D), U64($4715ED43E8A45C0A), U64($A8D7E4DAB780A08D),
    U64($0572B974F03CE0BB), U64($B57D2E985E1419C7), U64($E8D9ECBE2CF3D73F), U64($2FE4B17170E59750),
    U64($11317BA87905E790), U64($7FBF21EC8A1F45EC), U64($1725CABFCB045B00), U64($964E915CD5E2B207),
    U64($3E2B8BCBF016D66D), U64($BE7444E39328A0AC), U64($F85B2B4FBCDE44B7), U64($49353FEA39BA63B1),
    U64($1DD01AAFCD53486A), U64($1FCA8A92FD719F85), U64($FC7C95D827357AFA), U64($18A6A990C8B35EBD),
    U64($CCCB7005C6B9C28D), U64($3BDBB92C43B17F26), U64($AA70B5B4F89695A2), U64($E94C39A54A98307F),
    U64($B7A0B174CFF6F36E), U64($D4DBA84729AF48AD), U64($2E18BC1AD9704A68), U64($2DE0966DAF2F8B1C),
    U64($B9C11D5B1E43A07E), U64($64972D68DEE33360), U64($94628D38D0C20584), U64($DBC0D2B6AB90A559),
    U64($D2733C4335C6A72F), U64($7E75D99D94A70F4D), U64($6CED1983376FA72B), U64($97FCAACBF030BC24),
    U64($7B77497B32503B12), U64($8547EDDFB81CCB94), U64($79999CDFF70902CB), U64($CFFE1939438E9B24),
    U64($829626E3892D95D7), U64($92FAE24291F2B3F1), U64($63E22C147B9C3403), U64($C678B6D860284A1C),
    U64($5873888850659AE7), U64($0981DCD296A8736D), U64($9F65789A6509A440), U64($9FF38FED72E9052F),
    U64($E479EE5B9930578C), U64($E7F28ECD2D49EECD), U64($56C074A581EA17FE), U64($5544F7D774B14AEF),
    U64($7B3F0195FC6F290F), U64($12153635B2C0CF57), U64($7F5126DBBA5E0CA7), U64($7A76956C3EAFB413),
    U64($3D5774A11D31AB39), U64($8A1B083821F40CB4), U64($7B4A38E32537DF62), U64($950113646D1D6E03),
    U64($4DA8979A0041E8A9), U64($3BC36E078F7515D7), U64($5D0A12F27AD310D1), U64($7F9D1A2E1EBE1327),
    U64($DA3A361B1C5157B1), U64($DCDD7D20903D0C25), U64($36833336D068F707), U64($CE68341F79893389),
    U64($AB9090168DD05F34), U64($43954B3252DC25E5), U64($B438C2B67F98E5E9), U64($10DCD78E3851A492),
    U64($DBC27AB5447822BF), U64($9B3CDB65F82CA382), U64($B67B7896167B4C84), U64($BFCED1B0048EAC50),
    U64($A9119B60369FFEBD), U64($1FFF7AC80904BF45), U64($AC12FB171817EEE7), U64($AF08DA9177DDA93D),
    U64($1B0CAB936E65C744), U64($B559EB1D04E5E932), U64($C37B45B3F8D6F2BA), U64($C3A9DC228CAAC9E9),
    U64($F3B8B6675A6507FF), U64($9FC477DE4ED681DA), U64($67378D8ECCEF96CB), U64($6DD856D94D259236),
    U64($A319CE15B0B4DB31), U64($073973751F12DD5E), U64($8A8E849EB32781A5), U64($E1925C71285279F5),
    U64($74C04BF1790C0EFE), U64($4DDA48153C94938A), U64($9D266D6A1CC0542C), U64($7440FB816508C4FE),
    U64($13328503DF48229F), U64($D6BF7BAEE43CAC40), U64($4838D65F6EF6748F), U64($1E152328F3318DEA),
    U64($8F8419A348F296BF), U64($72C8834A5957B511), U64($D7A023A73260B45C), U64($94EBC8ABCFB56DAE),
    U64($9FC10D0F989993E0), U64($DE68A2355B93CAE6), U64($A44CFE79AE538BBE), U64($9D1D84FCCE371425),
    U64($51D2B1AB2DDFB636), U64($2FD7E4B9E72CD38C), U64($65CA5B96B7552210), U64($DD69A0D8AB3B546D),
    U64($604D51B25FBF70E2), U64($73AA8A564FB7AC9E), U64($1A8C1E992B941148), U64($AAC40A2703D9BEA0),
    U64($764DBEAE7FA4F3A6), U64($1E99B96E70A9BE8B), U64($2C5E9DEB57EF4743), U64($3A938FEE32D29981),
    U64($26E6DB8FFDF5ADFE), U64($469356C504EC9F9D), U64($C8763C5B08D1908C), U64($3F6C6AF859D80055),
    U64($7F7CC39420A3A545), U64($9BFB227EBDF4C5CE), U64($89039D79D6FC5C5C), U64($8FE88B57305E2AB6),
    U64($A09E8C8C35AB96DE), U64($FA7E393983325753), U64($D6B6D0ECC617C699), U64($DFEA21EA9E7557E3),
    U64($B67C1FA481680AF8), U64($CA1E3785A9E724E5), U64($1CFC8BED0D681639), U64($D18D8549D140CAEA),
    U64($4ED0FE7E9DC91335), U64($E4DBF0634473F5D2), U64($1761F93A44D5AEFE), U64($53898E4C3910DA55),
    U64($734DE8181F6EC39A), U64($2680B122BAA28D97), U64($298AF231C85BAFAB), U64($7983EED3740847D5),
    U64($66C1A2A1A60CD889), U64($9E17E49642A3E4C1), U64($EDB454E7BADC0805), U64($50B704CAB602C329),
    U64($4CC317FB9CDDD023), U64($66B4835D9EAFEA22), U64($219B97E26FFC81BD), U64($261E4E4C0A333A9D),
    U64($1FE2CCA76517DB90), U64($D7504DFA8816EDBB), U64($B9571FA04DC089C8), U64($1DDC0325259B27DE),
    U64($CF3F4688801EB9AA), U64($F4F5D05C10CAB243), U64($38B6525C21A42B0E), U64($36F60E2BA4FA6800),
    U64($EB3593803173E0CE), U64($9C4CD6257C5A3603), U64($AF0C317D32ADAA8A), U64($258E5A80C7204C4B),
    U64($8B889D624D44885D), U64($F4D14597E660F855), U64($D4347F66EC8941C3), U64($E699ED85B0DFB40D),
    U64($2472F6207C2D0484), U64($C2A1E7B5B459AEB5), U64($AB4F6451CC1D45EC), U64($63767572AE3D6174),
    U64($A59E0BD101731A28), U64($116D0016CB948F09), U64($2CF9C8CA052F6E9F), U64($0B090A7560A968E3),
    U64($ABEEDDB2DDE06FF1), U64($58EFC10B06A2068D), U64($C6E57A78FBD986E0), U64($2EAB8CA63CE802D7),
    U64($14A195640116F336), U64($7C0828DD624EC390), U64($D74BBE77E6116AC7), U64($804456AF10F5FB53),
    U64($EBE9EA2ADF4321C7), U64($03219A39EE587A30), U64($49787FEF17AF9924), U64($A1E9300CD8520548),
    U64($5B45E522E4B1B4EF), U64($B49C3B3995091A36), U64($D4490AD526F14431), U64($12A8F216AF9418C2),
    U64($001F837CC7350524), U64($1877B51E57A764D5), U64($A2853B80F17F58EE), U64($993E1DE72D36D310),
    U64($B3598080CE64A656), U64($252F59CF0D9F04BB), U64($D23C8E176D113600), U64($1BDA0492E7E4586E),
    U64($21E0BD5026C619BF), U64($3B097ADAF088F94E), U64($8D14DEDB30BE846E), U64($F95CFFA23AF5F6F4),
    U64($3871700761B3F743), U64($CA672B91E9E4FA16), U64($64C8E531BFF53B55), U64($241260ED4AD1E87D),
    U64($106C09B972D2E822), U64($7FBA195410E5CA30), U64($7884D9BC6CB569D8), U64($0647DFEDCD894A29),
    U64($63573FF03E224774), U64($4FC8E9560F91B123), U64($1DB956E450275779), U64($B8D91274B9E9D4FB),
    U64($A2EBEE47E2FBFCE1), U64($D9F1F30CCD97FB09), U64($EFED53D75FD64E6B), U64($2E6D02C36017F67F),
    U64($A9AA4D20DB084E9B), U64($B64BE8D8B25396C1), U64($70CB6AF7C2D5BCF0), U64($98F076A4F7A2322E),
    U64($BF84470805E69B5F), U64($94C3251F06F90CF3), U64($3E003E616A6591E9), U64($B925A6CD0421AFF3),
    U64($61BDD1307C66E300), U64($BF8D5108E27E0D48), U64($240AB57A8B888B20), U64($FC87614BAF287E07),
    U64($EF02CDD06FFDB432), U64($A1082C0466DF6C0A), U64($8215E577001332C8), U64($D39BB9C3A48DB6CF),
    U64($2738259634305C14), U64($61CF4F94C97DF93D), U64($1B6BACA2AE4E125B), U64($758F450C88572E0B),
    U64($959F587D507A8359), U64($B063E962E045F54D), U64($60E8ED72C0DFF5D1), U64($7B64978555326F9F),
    U64($FD080D236DA814BA), U64($8C90FD9B083F4558), U64($106F72FE81E2C590), U64($7976033A39F7D952),
    U64($A4EC0132764CA04B), U64($733EA705FAE4FA77), U64($B4D8F77BC3E56167), U64($9E21F4F903B33FD9),
    U64($9D765E419FB69F6D), U64($D30C088BA61EA5EF), U64($5D94337FBFAF7F5B), U64($1A4E4822EB4D7A59),
    U64($6FFE73E81B637FB3), U64($DDF957BC36D8B9CA), U64($64D0E29EEA8838B3), U64($08DD9BDFD96B9F63),
    U64($087E79E5A57D1D13), U64($E328E230E3E2B3FB), U64($1C2559E30F0946BE), U64($720BF5F26F4D2EAA),
    U64($B0774D261CC609DB), U64($443F64EC5A371195), U64($4112CF68649A260E), U64($D813F2FAB7F5C5CA),
    U64($660D3257380841EE), U64($59AC2C7873F910A3), U64($E846963877671A17), U64($93B633ABFA3469F8),
    U64($C0C0F5A60EF4CDCF), U64($CAF21ECD4377B28C), U64($57277707199B8175), U64($506C11B9D90E8B1D),
    U64($D83CC2687A19255F), U64($4A29C6465A314CD1), U64($ED2DF21216235097), U64($B5635C95FF7296E2),
    U64($22AF003AB672E811), U64($52E762596BF68235), U64($9AEBA33AC6ECC6B0), U64($944F6DE09134DFB6),
    U64($6C47BEC883A7DE39), U64($6AD047C430A12104), U64($A5B1CFDBA0AB4067), U64($7C45D833AFF07862),
    U64($5092EF950A16DA0B), U64($9338E69C052B8E7B), U64($455A4B4CFE30E3F5), U64($6B02E63195AD0CF8),
    U64($6B17B224BAD6BF27), U64($D1E0CCD25BB9C169), U64($DE0C89A556B9AE70), U64($50065E535A213CF6),
    U64($9C1169FA2777B874), U64($78EDEFD694AF1EED), U64($6DC93D9526A50E68), U64($EE97F453F06791ED),
    U64($32AB0EDB696703D3), U64($3A6853C7E70757A7), U64($31865CED6120F37D), U64($67FEF95D92607890),
    U64($1F2B1D1F15F6DC9C), U64($B69E38A8965C6B65), U64($AA9119FF184CCCF4), U64($F43C732873F24C13),
    U64($FB4A3D794A9A80D2), U64($3550C2321FD6109C), U64($371F77E76BB8417E), U64($6BFA9AAE5EC05779),
    U64($CD04F3FF001A4778), U64($E3273522064480CA), U64($9F91508BFFCFC14A), U64($049A7F41061A9E60),
    U64($FCB6BE43A9F2FE9B), U64($08DE8A1C7797DA9B), U64($8F9887E6078735A1), U64($B5B4071DBFC73A66),
    U64($230E343DFBA08D33), U64($43ED7F5A0FAE657D), U64($3A88A0FBBCB05C63), U64($21874B8B4D2DBC4F),
    U64($1BDEA12E35F6A8C9), U64($53C065C6C8E63528), U64($E34A1D250E7A8D6B), U64($D6B04D3B7651DD7E),
    U64($5E90277E7CB39E2D), U64($2C046F22062DC67D), U64($B10BB459132D0A26), U64($3FA9DDFB67E2F199),
    U64($0E09B88E1914F7AF), U64($10E8B35AF3EEAB37), U64($9EEDECA8E272B933), U64($D4C718BC4AE8AE5F),
    U64($81536D601170FC20), U64($91B534F885818A06), U64($EC8177F83F900978), U64($190E714FADA5156E),
    U64($B592BF39B0364963), U64($89C350C893AE7DC1), U64($AC042E70F8B383F2), U64($B49B52E587A1EE60),
    U64($FB152FE3FF26DA89), U64($3E666E6F69AE2C15), U64($3B544EBE544C19F9), U64($E805A1E290CF2456),
    U64($24B33C9D7ED25117), U64($E74733427B72F0C1), U64($0A804D18B7097475), U64($57E3306D881EDB4F),
    U64($4AE7D6A36EB5DBCB), U64($2D8D5432157064C8), U64($D1E649DE1E7F268B), U64($8A328A1CEDFE552C),
    U64($07A3AEC79624C7DA), U64($84547DDC3E203C94), U64($990A98FD5071D263), U64($1A4FF12616EEFC89),
    U64($F6F7FD1431714200), U64($30C05B1BA332F41C), U64($8D2636B81555A786), U64($46C9FEB55D120902),
    U64($CCEC0A73B49C9921), U64($4E9D2827355FC492), U64($19EBB029435DCB0F), U64($4659D2B743848A2C),
    U64($963EF2C96B33BE31), U64($74F85198B05A2E7D), U64($5A0F544DD2B1FB18), U64($03727073C2E134B1),
    U64($C7F6AA2DE59AEA61), U64($352787BAA0D7C22F), U64($9853EAB63B5E0B35), U64($ABBDCDD7ED5C0860),
    U64($CF05DAF5AC8D77B0), U64($49CAD48CEBF4A71E), U64($7A4C10EC2158C4A6), U64($D9E92AA246BF719E),
    U64($13AE978D09FE5557), U64($730499AF921549FF), U64($4E4B705B92903BA4), U64($FF577222C14F0A3A),
    U64($55B6344CF97AAFAE), U64($B862225B055B6960), U64($CAC09AFBDDD2CDB4), U64($DAF8E9829FE96B5F),
    U64($B5FDFC5D3132C498), U64($310CB380DB6F7503), U64($E87FBB46217A360E), U64($2102AE466EBB1148),
    U64($F8549E1A3AA5E00D), U64($07A69AFDCC42261A), U64($C4C118BFE78FEAAE), U64($F9F4892ED96BD438),
    U64($1AF3DBE25D8F45DA), U64($F5B4B0B0D2DEEEB4), U64($962ACEEFA82E1C84), U64($046E3ECAAF453CE9),
    U64($F05D129681949A4C), U64($964781CE734B3C84), U64($9C2ED44081CE5FBD), U64($522E23F3925E319E),
    U64($177E00F9FC32F791), U64($2BC60A63A6F3B3F2), U64($222BBFAE61725606), U64($486289DDCC3D6780),
    U64($7DC7785B8EFDFC80), U64($8AF38731C02BA980), U64($1FAB64EA29A2DDF7), U64($E4D9429322CD065A),
    U64($9DA058C67844F20C), U64($24C0E332B70019B0), U64($233003B5A6CFE6AD), U64($D586BD01C5C217F6),
    U64($5E5637885F29BC2B), U64($7EBA726D8C94094B), U64($0A56A5F0BFE39272), U64($D79476A84EE20D06),
    U64($9E4C1269BAA4BF37), U64($17EFEE45B0DEE640), U64($1D95B0A5FCF90BC6), U64($93CBE0B699C2585D),
    U64($65FA4F227A2B6D79), U64($D5F9E858292504D5), U64($C2B5A03F71471A6F), U64($59300222B4561E00),
    U64($CE2F8642CA0712DC), U64($7CA9723FBB2E8988), U64($2785338347F2BA08), U64($C61BB3A141E50E8C),
    U64($150F361DAB9DEC26), U64($9F6A419D382595F4), U64($64A53DC924FE7AC9), U64($142DE49FFF7A7C3D),
    U64($0C335248857FA9E7), U64($0A9C32D5EAE45305), U64($E6C42178C4BBB92E), U64($71F1CE2490D20B07),
    U64($F1BCC3D275AFE51A), U64($E728E8C83C334074), U64($96FBF83A12884624), U64($81A1549FD6573DA5),
    U64($5FA7867CAF35E149), U64($56986E2EF3ED091B), U64($917F1DD5F8886C61), U64($D20D8C88C8FFE65F),
    U64($31D71DCE64B2C310), U64($F165B587DF898190), U64($A57E6339DD2CF3A0), U64($1EF6E6DBB1961EC9),
    U64($70CC73D90BC26E24), U64($E21A6B35DF0C3AD7), U64($003A93D8B2806962), U64($1C99DED33CB890A1),
    U64($CF3145DE0ADD4289), U64($D0E4427A5514FB72), U64($77C621CC9FB3A483), U64($67A34DAC4356550B),
    U64($F8D626AAAF278509)
);

  RandomPiece: ^UInt64 = @Random64[0];
  RandomCastle: ^UInt64 = @Random64[768];
  RandomEnPassant: ^UInt64 = @Random64[772];
  RandomTurn: ^UInt64 = @Random64[780];
  piece_names = 'pPnNbBrRqQkK';

function hash(const fen: string): UInt64;
var
  board_s: string;
  to_move_c: char;
  castle_flags_s: string;
  ep_square_s: string;
  board: array[0..7, 0..7] of char;
  c: char;
  p, r, f, i, p_enc: integer;
begin
  result := 0;
  
  with TStringList.Create do
  try
    DelimitedText := fen;
    board_s := Strings[0];
    to_move_c := Strings[1][1];
    castle_flags_s := Strings[2];
    ep_square_s := Strings[3];
  finally
    Free;
  end;
  
  r := 7;
  f := 0;
  p := 1;
  while p <= Length(board_s) do
  begin
    c := board_s[p];
    Inc(p);
    if c = '/' then
    begin
      Dec(r);
      f := 0;
      Continue;
    end;
    if (c >= '1') and (c <= '8') then
    begin
      for i := 0 to Ord(c) - Ord('1') do
      begin
        board[f][r] := '-';
        Inc(f);
      end;
      Continue;
    end;
    board[f][r] := c;
    Inc(f);
  end;
  
  for f := 0 to 7 do
    for r := 0 to 7 do
    begin
      c := board[f][r];
      if c <> '-' then
      begin
        p_enc := Pred(Pos(c, piece_names));
        result := result xor RandomPiece[64 * p_enc + 8 * r + f];
      end;
    end;
  
  p := 1;
  while p <= Length(castle_flags_s) do
  begin
    c := castle_flags_s[p];
    Inc(p);
    case c of
      'K': result := result xor RandomCastle[0];
      'Q': result := result xor RandomCastle[1];
      'k': result := result xor RandomCastle[2];
      'q': result := result xor RandomCastle[3];
    end;
  end;
  
  if ep_square_s[1] <> '-' then
  begin
    f := Ord(ep_square_s[1]) - Ord('a');
    if to_move_c = 'b' then
    begin
      if ((f > 0) and (board[f - 1][3] = 'p'))
      or ((f < 7) and (board[f + 1][3] = 'p')) then
        result := result xor RandomEnPassant[f];
    end else
    begin
      if ((f > 0) and (board[f - 1][4] = 'P'))
      or ((f < 7) and (board[f + 1][4] = 'P')) then
        result := result xor RandomEnPassant[f];
    end;
  end;
  
  if to_move_c = 'w' then
    result := result xor RandomTurn[0];
end;

type
  entry_t = record
    key: UInt64;
    move: UInt16;
    weight: UInt16;
    learn: UInt32;
  end;

const
  entry_none: entry_t = (
    key: 0;
    move: 0;
    weight: 0;
    learn: 0
  );
  promote_pieces = 'nbrq';
  MAX_MOVES = 100;

function int_from_file(const f: THandle; const l: integer; var r: UInt64): boolean;
var
  i: integer;
  c: byte;
begin
  for i := 0 to Pred(l) do
  begin
    if FileRead(f, c, SizeOf(c)) <> SizeOf(c) then
      Exit(FALSE);
    r := (r shl 8) + c;
  end;
  result := TRUE;
end;

function entry_from_file(const f: THandle; var entry: entry_t): boolean;
var
  r: UInt64 = 0;
begin
  if not int_from_file(f, 8, r) then Exit(FALSE);
  entry.key := r;
  if not int_from_file(f, 2, r) then Exit(FALSE);
  entry.move := UInt16(r);
  if not int_from_file(f, 2, r) then Exit(FALSE);
  entry.weight := UInt16(r);
  if not int_from_file(f, 4, r) then Exit(FALSE);
  entry.learn := UInt32(r);
  result := TRUE;
end;

function find_key(const f: THandle; const key: UInt64; var entry: entry_t): integer;
var
  first, last, middle: integer;
  first_entry, last_entry, middle_entry: entry_t;
begin
  first_entry := entry_none;
  first := -1;
  if FileSeek(f, -16, fsFromEnd) = -1 then
  begin
    entry := entry_none;
    entry.key := key + 1; // hack
    Exit(-1);
  end;
  last := FileSeek(f, 0, fsFromCurrent) div 16;
  entry_from_file(f, last_entry);
  while TRUE do
  begin
    if last - first = 1 then
    begin
      entry := last_entry;
      Exit(last);
    end;
    middle := (first + last) div 2;
    FileSeek(f, 16 * middle, fsFromBeginning);
    entry_from_file(f, middle_entry);
    if key <= middle_entry.key then
    begin
      last := middle;
      last_entry := middle_entry;
    end else
    begin
      first := middle;
      first_entry := middle_entry;
    end;
  end;
end;

procedure move_to_string(var move_s: string; const move: UInt16);
var
  f, fr, ff, t, tr, tf, p: integer;
begin
  f := (move shr 6) and 63;
  fr := (f shr 3) and 7;
  ff := f and 7;
  t := move and 63;
  tr := (t shr 3) and 7;
  tf := t and 7;
  p := (move shr 12) and 7;
  SetLength(move_s, 4);
  move_s[1] := Chr(ff + Ord('a'));
  move_s[2] := Chr(fr + Ord('1'));
  move_s[3] := Chr(tf + Ord('a'));
  move_s[4] := Chr(tr + Ord('1'));
  if p <> 0 then
  begin
    SetLength(move_s, 5);
    move_s[5] := promote_pieces[p];
  end;
  if      move_s = 'e1h1' then
    move_s := 'e1g1'
  else if move_s = 'e1a1' then
    move_s := 'e1c1'
  else if move_s = 'e8h8' then
    move_s := 'e8g8'
  else if move_s = 'e8a8' then
    move_s := 'e8c8'; 
end;

function PolyglotKey(const APosition: string): UInt64;
begin
  result := hash(APosition);
end;

function BestMove(const AKey: UInt64; const ABookName: TFileName): string;
var
  f: THandle;
  entry: entry_t;
  offset: longint;
  entries: array[0..MAX_MOVES - 1] of entry_t;
  count: longint = 0;
  i: longint;
  move_s: string;
  total_weight: longint;
begin
  f := FileOpen(ABookName, fmOpenRead);
  if f = THandle(-1) then
  begin
{$IFDEF LINUX}
    WriteLn(ErrOutput, 'BOOK Cannot open file ', ABookName);
{$ENDIF}
    Exit(EmptyStr);
  end;
  offset := find_key(f, AKey, entry);
  if entry.key <> AKey then
  begin
{$IFDEF LINUX}
    WriteLn(ErrOutput, 'BOOK No such key');
{$ENDIF}
    FileClose(f);
    Exit(EmptyStr);
  end;
  entries[0] := entry;
  count := 1;
  FileSeek(f, 16 * (offset + 1), fsFromBeginning);
  while TRUE do
  begin
    if not entry_from_file(f, entry) then
      Break;
    if entry.key <> AKey then
      Break;
    if count = MAX_MOVES then
    begin
{$IFDEF LINUX}
      WriteLn(ErrOutput, 'BOOK Too many moves');
{$ENDIF}
      FileClose(f);
      Exit(EmptyStr);
    end;
    entries[count] := entry;
    Inc(count);
  end;
  FileClose(f);
{$IFDEF DEBUG}
  total_weight := 0;
  for i := 0 to Pred(count) do
    Inc(total_weight, entries[i].weight);
  for i := 0 to Pred(count) do
  begin
    move_to_string(move_s, entries[i].move);
{$IFDEF LINUX}
    WriteLn(ErrOutput, Format('BOOK move=%s weight=%5.2f%%', [move_s, 100 * (entries[i].weight / total_weight)]));
{$ENDIF}
  end;
{$ENDIF}
  move_to_string(move_s, entries[{0}Random(count)].move);
  result := move_s;
end;

function BestMove(const APosition: string; const ABookName: TFileName): string;
var
  LKey: UInt64;
begin
  LKey := PolyglotKey(APosition);
  result := BestMove(LKey, ABookName);
end;

end.
